package com.ssmr.demo.service;

import com.ssmr.demo.dao.TProductMapper;
import com.ssmr.demo.entity.TProduct;
import com.ssmr.demo.entity.TProductUserOneToOne;
import com.ssmr.demo.entity.TUser;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class TProductServiceImpl implements TProductService {
    @Resource
    private TProductMapper tProductMapper;
    @Resource
    private RedisTemplate redisTemplate;
    private static final String SECKILL_PREFIX = "seckill_";

    @Override
    @Transactional
    @CacheEvict(value = "redisCacheManager", key = "'redis_cache_product_'+#idtProduct", beforeInvocation = false)
    public int deleteByPrimaryKey(Integer idtProduct) {
        return tProductMapper.deleteByPrimaryKey(idtProduct);
    }

    @Override
    @Transactional
    public int insert(TProduct record) {
        return tProductMapper.insert(record);
    }

    @Override
    @Transactional
    @CachePut(value = "redisCacheManager", key = "'redis_cache_product_'+#record.idtProduct")
    public TProduct insertAndReturn(TProduct record) {
        tProductMapper.insert(record);
        return record;
    }


    /*https://blog.csdn.net/qq_34021712/article/details/79606551
    https://blog.csdn.net/weixin_42473560/article/details/85161676
     * RedisTemplate 和 @Transactional 共存的问题，最好备两个RedisTemplate. 或者用SessionCallBack和RedisTemplate操作。*/
    @Override
    @Transactional
    /*这个@Transactional对于方法内的redistemplate无效，因为没有开启redisTemplate.setEnableTransactionSupport(true).
    1.如果开启了，对于数据库操作和redistemplate操作都有效，可以回滚，但是redistemplate事务里的get操作是得不到值的，和multi exec 类似。
    2.另一种方法是自己手动RedisTemplate+SessionCallback+watch,multi,exec, 更灵活更麻烦，但是注意命令是进队列的，multi里get是null.*/
    public TProduct insertAndReturnUsingRedisTemplate(TProduct tProduct) {
        tProductMapper.insert(tProduct);
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("idtProduct", String.valueOf(tProduct.getIdtProduct()));
        hashMap.put("product_name", tProduct.getProductName());
        hashMap.put("stock", String.valueOf(tProduct.getStock()));
        hashMap.put("price", String.valueOf(tProduct.getPrice()));
        hashMap.put("version", String.valueOf(tProduct.getVersion()));
        /*SessionCallback+事务操作，都在同一个连接中*/
        Object executeResult = redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations) throws DataAccessException {
                List execList = null;
                while (execList == null) {
                    /*这里也可以watch某个key，必须在multi之前读取出来*/
                    redisOperations.multi();
                    redisOperations.opsForHash().putAll(SECKILL_PREFIX + String.valueOf(tProduct.getIdtProduct()), hashMap);
                    redisOperations.opsForValue().set(SECKILL_PREFIX + String.valueOf(tProduct.getIdtProduct()) + "_version", String.valueOf(tProduct.getVersion()));
                    try {
                        /*如果需要返回结果，如get， 可以返回List*/
                        execList = redisOperations.exec();
                    } catch (Exception e) {
                        /*事务出错会捕捉错误*/
                    }
                }
                return execList;
            }
        });
        return tProduct;
    }
    /*使用 redisTemplate 而不是 RedisCacheManager 的select, update方法待补…… */

    @Override
    @Transactional
    @Cacheable(value = "redisCacheManager", key = "'redis_cache_product_'+#idtProduct")
    public TProduct selectByPrimaryKey(Integer idtProduct) {
        return tProductMapper.selectByPrimaryKey(idtProduct);
    }

    @Override
    public List<TProduct> selectAll() {
        return tProductMapper.selectAll();
    }

    @Override
    @Transactional
    public int updateByPrimaryKey(TProduct record) {
        return tProductMapper.updateByPrimaryKey(record);
    }

    @Override
    @CachePut(value = "redisCacheManager", key = "'redis_cache_product_'+#record.idtProduct")
    public TProduct updateByPrimaryKeyAndReturn(TProduct record) {
        tProductMapper.updateByPrimaryKey(record);
        return record;
    }

    @Override
    public TProductUserOneToOne selectTProductUseOneToOne(Integer idtProduct) {
        return tProductMapper.selectTProductUseOneToOne(idtProduct);
    }

    @Override
    public TUser selectUserbyId(Integer userId) {
        return tProductMapper.selectUserbyId(userId);
    }
}
